#include "main.h"

/* 
 * Static flag variable to track application running status
 * This flag indicates whether the main application is currently executing
 * Used for state management and preventing multiple simultaneous executions
 * Initialized to RESET state indicating application is not running
 */
static uint8_t flag_is_running = RESET;

/* 
 * Volatile token seed for generating unique message identifiers
 * This variable provides a sequentially increasing token for MQTT messages
 * Volatile qualifier ensures proper handling in multi-threaded environments
 * Used to generate unique tokens for message correlation and tracking
 */
volatile uint16_t plc_tokenSeed = 1;

/**
 * @brief Generate unique token for message identification
 * 
 * This function creates sequentially increasing token values for MQTT messages
 * Provides unique identifiers for request-response message correlation
 * Implements wrap-around logic to prevent token value overflow
 * Tokens are used to match requests with their corresponding responses
 * 
 * @param token Character buffer to store the generated token string
 */
void get_token(char *token)
{
	/* Check if token seed is approaching maximum value */
	if (plc_tokenSeed >= 65535)
		/* Reset token seed to prevent overflow */
		plc_tokenSeed = 0;

	/* Convert token seed integer to string representation */
	sprintf(token, "%d", plc_tokenSeed);

	/* Increment token seed for next token generation */
	plc_tokenSeed++;
}

/**
 * @brief Convert integer value to Binary Coded Decimal format
 * 
 * This function transforms an 8-bit integer into BCD representation
 * Separates the tens and units digits into high and low nibbles
 * Used for numerical data encoding in communication protocols
 * Supports values from 0 to 99 for proper BCD conversion
 * 
 * @param data Integer value to convert (0-99 range)
 * @return uint8_t BCD encoded value with tens and units in separate nibbles
 * @note Input values above 99 may produce unexpected results
 */
uint8_t itobcd(uint8_t data)
{
	/* 
	 * Convert integer to BCD format:
	 * - Extract tens digit, shift to high nibble (bits 4-7)
	 * - Extract units digit, place in low nibble (bits 0-3)
	 * - Combine both nibbles into single byte
	 */
	return (((data / 10 << 4) & 0xf0) | ((data % 10) & 0x0f));
}

/**
 * @brief Generate ISO 8601 formatted timestamp string
 * 
 * This function retrieves current system time and formats it as timestamp
 * Creates human-readable timestamp in YYYY-MM-DDTHH:MM:SS format
 * Used for message timestamping and temporal data correlation
 * Provides consistent time formatting across the application
 * 
 * @param psDateTime Character buffer to store formatted timestamp string
 * @retval None
 * @note Buffer must be large enough to hold 19 characters plus null terminator
 */
void mqtt_get_time(char *psDateTime)
{
	/* Structure for storing time with microsecond precision */
	struct timeval tv;
	/* Structure for timezone information (unused but required) */
	struct timezone tz;
	/* Pointer to broken-down time structure */
	struct tm *pTM;

	/* Retrieve current time from system clock */
	gettimeofday(&tv, &tz);
	/* Convert seconds since epoch to local time structure */
	pTM = localtime(&tv.tv_sec);

	/* Format time components into ISO 8601 string */
	sprintf(psDateTime, "%04d-%02d-%02dT%02d:%02d:%02d",
			pTM->tm_year + 1900, pTM->tm_mon + 1, pTM->tm_mday,
			pTM->tm_hour, pTM->tm_min, pTM->tm_sec);
}

/**
 * @brief Check application running status
 * 
 * This function returns the current execution state of the application
 * Used by monitoring functions to determine if main application is active
 * Provides thread-safe status checking for system management
 * 
 * @param running Unused parameter (maintains interface consistency)
 * @retval uint8_t Current running status (SET = running, RESET = stopped)
 */
uint8_t is_app_running(void)
{
	/* Return current application running status flag */
	return flag_is_running;
}

/**
 * @brief Set application running status flag
 * 
 * This function updates the application execution state indicator
 * Used during application startup and shutdown sequences
 * Provides controlled state transitions for application lifecycle
 * 
 * @param running New running status value (SET or RESET)
 * @retval None
 */
void set_app_running(uint8_t running)
{
	/* Update application running status with new value */
	flag_is_running = running;
}

/**
 * @brief Implement millisecond precision delay
 * 
 * This function provides precise timing delays using select system call
 * More accurate than sleep() for sub-second timing requirements
 * Handles signal interruptions gracefully with automatic retry
 * Used for polling delays and timing-sensitive operations
 * 
 * @param mSec Number of milliseconds to delay execution
 */
void delay_ms(unsigned long mSec)
{
	/* Timeval structure for specifying delay duration */
	struct timeval tv;
	/* Convert milliseconds to seconds and microseconds */
	tv.tv_sec = mSec / 1000;
	tv.tv_usec = (mSec % 1000) * 1000;
	/* Variable to track select system call result */
	int err;
	/* 
	 * Use select for precise timing with automatic interrupt handling
	 * Loop ensures delay completes even if interrupted by signals
	 */
	do
	{
		/* Select with NULL file descriptor sets creates pure delay */
		err = select(0, NULL, NULL, NULL, &tv);
	} while (err < 0 && errno == EINTR); /* Retry if interrupted by signal */
}

/**
 * @brief Convert ASCII hexadecimal string to binary data
 * 
 * This function parses hexadecimal ASCII characters into binary bytes
 * Supports both uppercase and lowercase hexadecimal digits (A-F, a-f)
 * Handles strings of even or odd length with proper nibble alignment
 * Used for protocol data conversion and binary payload processing
 * 
 * @param str Input string containing hexadecimal ASCII characters
 * @param out Output buffer for converted binary data
 * @param outlen Unused parameter (maintains interface compatibility)
 * @retval int Number of bytes successfully converted and stored
 */
int AtoH(char *str, uint8_t *out)
{
	/* Local pointer for string traversal */
	char *p = str;

	/* Variables for storing high and low nibbles during conversion */
	char high = 0;
	char low = 0;

	/* Variable for tracking input string length */
	int tmplen = 0;
	/* Counter for tracking output byte position */
	int cnt = 0;

	/* Validate input parameters are not null pointers */
	if ((str == NULL) || ((out == NULL)))
	{
		/* Return zero to indicate conversion failure */
		return 0;
	}

	/* Calculate length of input hexadecimal string */
	tmplen = strlen(p);

	/* Check for empty input string */
	if (tmplen == 0)
	{
		/* Return zero for empty input */
		return 0;
	}

	/* Process string in pairs of characters (two hex digits per byte) */
	while (cnt < (tmplen / 2))
	{
		/* 
		 * Convert first character (high nibble):
		 * - Adjust for hex letters (A-F/a-f) by subtracting additional 7
		 * - For digits (0-9), simple subtraction of '0' character
		 */
		high = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ? *p - 48 - 7 : *p - 48;
		/* 
		 * Convert second character (low nibble):
		 * - Pre-increment pointer to next character
		 * - Apply same conversion logic as high nibble
		 */
		low = (*(++p) > '9' && ((*p <= 'F') || (*p <= 'f'))) ? *(p)-48 - 7 : *(p)-48;

		/* 
		 * Combine high and low nibbles into single byte:
		 * - Shift high nibble to upper 4 bits
		 * - Place low nibble in lower 4 bits
		 * - Store combined byte in output buffer
		 */
		out[cnt] = ((high & 0x0f) << 4 | (low & 0x0f));

		/* Advance to next character pair */
		p++;
		/* Increment output byte counter */
		cnt++;
	}

	/* Handle odd-length strings (single remaining character) */
	if (tmplen % 2 != 0)
	{
		/* 
		 * Convert single remaining character to nibble:
		 * - Apply same conversion logic as paired characters
		 * - Store in high nibble, low nibble remains zero
		 */
		out[cnt] = ((*p > '9') && ((*p <= 'F') || (*p <= 'f'))) ? *p - 48 - 7 : *p - 48;
	}

	/* Return total number of bytes produced by conversion */
	return tmplen / 2 + tmplen % 2;
}

/**
 * @brief Retrieve current system time in microseconds
 * 
 * This function provides high-resolution timing using system clock
 * Combines seconds and microseconds for precise time measurements
 * Used for performance monitoring and time-sensitive operations
 * Provides microsecond precision for accurate timing calculations
 * 
 * @return uint64_t Current system time in microseconds since epoch
 */
uint64_t get_system_time_us(void)
{
	/* Structure for storing time with microsecond precision */
	struct timeval now;

	/* Retrieve current time from system clock */
	gettimeofday(&now, NULL);

	/* 
	 * Convert time to microseconds:
	 * - Multiply seconds by 1,000,000 for microsecond conversion
	 * - Add microseconds component directly
	 * - Return combined 64-bit microsecond timestamp
	 */
	return (uint64_t)(now.tv_sec * 1000000L + now.tv_usec);
}

/**
 * @brief Calculate absolute time difference between two timestamps
 * 
 * This function computes the time elapsed between two microsecond timestamps
 * Handles timer wrap-around correctly by using 64-bit arithmetic
 * Provides reliable time interval calculations for long-running processes
 * Used for timeout detection and performance interval measurements
 * 
 * @param current Later timestamp in microseconds
 * @param last Earlier timestamp in microseconds  
 * @return uint64_t Absolute time difference in microseconds
 */
uint64_t get_system_time_absoffset(uint64_t current, uint64_t last)
{
	/* 
	 * Calculate time difference with wrap-around protection:
	 * - Normal case: current time is greater than last time
	 * - Wrap case: handle timer overflow by calculating remaining space
	 */
	if (current >= last)
		/* Simple subtraction for normal time progression */
		return (current - last);
	else
		/* 
		 * Handle timer wrap-around:
		 * - Calculate time from last to maximum value
		 * - Add time from zero to current value
		 * - 0xffffffffffffffff represents maximum 64-bit value
		 */
		return (uint64_t)((0xffffffffffffffff - last) + current);
}